{
 "cells": [
  {
   "cell_type": "markdown",
   "source": [
    "# 算法简介\n",
    "\n",
    "基于规则的分类器是**使用一组\"if...then...\"规则来对记录进行分类的技术。**\n"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "模型的规则用 $R =(r_1 ∨ r_2 ∨ ••• ∨ r_k)$表示"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    ">n其中R称作规则集，$r_i$ 是分类规则。"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "每一个分类规则可以表示为如下形式："
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    ">n$r_i:(条件i)→y_i$"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "规则右边称为**规则后件，包含预测类$y_i$。** 规则左边称为**规则前件或前提。**\n",
    "\n",
    "它是**属性和属性值的合取**：$条件i=(A_1\\, op \\,\\,  v_1)∧(A_2\\,  op \\,\\,  v_2)∧•••∧(A_n \\, op\\,\\,   v_n)$"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "其中$(A_j，v_j)$是属性-值对，op是比较运算符，取自集合${=，≠，﹤，﹥，≤，≥}$。\n",
    "\n",
    "每一个属性和属性值$(A_j \\, \\, op\\, \\,  v_j)$称为一个合取项。"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "如果规则$r$的前件和记录$x$的属性匹配，则称**$r$覆盖$x$**。\n",
    "\n",
    "当$r$覆盖给定的记录时，称**$r$被触发**。当所有规则中只有规则$r$被触发，则称**$r$被激活**。"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "分类规则的质量可以用**覆盖率（coverage）和准确率（accuracy）**来度量。\n",
    "\n",
    "给定数据集D和分类规则 $r：A→y$，规则的覆盖率定义为**D中触发规则r的记录所占的比例。**\n",
    "\n",
    "准确率或置信因子定义为**触发$r$的记录中类标号等于$y$的记录所占的比例。**"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "$Coverage(r)= |A| / |D|$\n",
    "\n",
    "$Accuracy(r)= |A∩y| / |A|$\n",
    "\n",
    "其中$|A|$是**满足规则前件的记录数**，$|A∩y|$是**同时满足规则前件和后件的记录数，D是记录总数。**"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "# 工作原理"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "### 基于规则的分类器所产生的规则集的两个重要性质：\n",
    "\n",
    ">互斥规则：如果规则集R中不存在两条规则被同一条记录触发，则称规则集R中的规则是互斥的。这个性质确保每条记录至多被R中的一条规则覆盖。\n",
    "\n",
    ">穷举规则：如果对属性值的任意组合，R中都存在一条规则加以覆盖，则称规则集R具有穷举覆盖。这个性质确保每一条记录都至少被R中的一条规则覆盖。\n",
    "\n",
    "**这两个性质共同作用，保证每一条记录被且仅被一条规则覆盖。**\n",
    "\n",
    "如果规则集**不是穷举的**，那么必须添加一个默认规则 $r_d:() → y_d$来覆盖那些未被覆盖的记录。\n",
    "\n",
    "默认规则的前件为空，当所有其他规则失效时触发。$y_d$是默认类，通常被指定为没有被现存规则覆盖的训练记录的多数类。\n",
    "\n"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "如果规则集**不是互斥的**，那么一条记录可能被多条规则覆盖，这些规则的预测可能会相互冲突，解决这个问题有如下两种方法：\n",
    "\n",
    ">1、有序规则：规则集中的规则按照优先级降序排列，优先级的定义有多种方法（如基于准确率、覆盖率、总描述长度或规则产生的顺序等）。\n",
    "\n",
    ">有序规则的规则集也称为决策表。当测试记录出现时，由覆盖记录的最高秩的规则对其进行分类，这就避免由多条分类规则来预测而产生的类冲突的问题。\n",
    "\n",
    ">2、无序规则：允许一条测试记录触发多条分类规则，把每条被触发规则的后件看作是对相应类的一次投票，然后计票确定测试记录的类标号。\n",
    "\n",
    ">通常把记录指派到得票最多的类。\n",
    "\n"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "本文重点讨论**使用有序规则的基于规则的分类器。**"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "# 如果被触发的多个规则指向不同的类"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "解决这个问题的核心思想，就是需要对不同规则进行优先级排序，取优先级高的规则所对应的类别作为元组的分类。 "
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "两种解决方案： "
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "\n",
    "**规模序**：把最高优先级权赋予具有“最苛刻”要求的被触发的规则，其中苛刻性用规则前件的规模度量。也就是说，激活具有最多属性测试的被触发的规则。\n",
    "\n",
    "**规则序**：指预先确定规则的优先次序。这种序的确定可以基于两种方法：基于类的序和基于规则的序 "
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "# 规则的排序方案"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "对规则的排序可以逐条规则进行或者逐个类进行。\n",
    "\n",
    "**基于规则质量的排序方案**：这个方案依据规则质量的某种度量对规则进行排序。这种排序方案确保每一个测试记录都是由覆盖它的“最好的”规则来分类。这种规则的质量可以是准确度、覆盖率或规模等。该方案的潜在缺点是**规则的秩（规则合取项的个数）越低越难解释，因为每个规则都假设所有排在它前面的规则不成立。**\n",
    "\n",
    "**基于类标号的排序方案**：根据类的重要性来对规则进行排序。即最重要的类对应的规则先出现，次重要的类对应的规则紧接着出现，以此类推。对于类的重要性的衡量标准有很多，比如类的普遍性、误分类代价等。这使得规则的解释稍微容易一些。在这种方案中，属于同一个分类的规则在规则集R中一起出现。然后，这些规则根据它们所属的分类信息一起排序。同一个分类的规则之间的相对顺序并不重要，只要其中一个规则被激发，类标号就会赋给测试记录。然而，质量较差的规则可能碰巧预测较高秩的类，从而导致高质量的规则被忽略。\n",
    "\n",
    "大部分基于规则的分类器（如CS4.5规则和RIPPER）都采用**基于类标号的排序方案。**"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "# 如何建立基于规则的分类器"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "为了建立基于规则的分类器，需要提取一组规则来识别数据集的属性和类标号之间的关键联系。提取分类规则的方法有两大类：\n",
    "\n",
    ">（1）直接方法，直接从数据中提取分类规则；\n",
    "\n",
    ">（2）间接方法，从其他分类模型（如决策树和神经网络）中提取分类规则。\n",
    "\n",
    "直接方法把属性空间分为较小的子空间，以便于属于一个子空间的所有记录可以使用一个分类规则进行分类。\n",
    "\n",
    "间接方法使用分类规则为较复杂的分类模型提供简洁的描述。\n"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "# 规则提取的直接方法"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "顺序覆盖：算法经常被用来从直接数据中提取规则，规则基于某种评估度量以**贪心**的方式增长。\n",
    "\n",
    "该算法从包含多个类的数据集中一次提取一个类的规则。决定哪一个类的规则最先产生的标准取决于多种因素，如类的普遍性（即训练记录中属于特定类的记录的比例），或者给定类中误分类记录的代价。"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "# 1.  Learn-One-Rule函数"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "Learn-One-Rule函数的目标是**提取一个分类规则，该规则覆盖训练集中的大量正例，没有或仅覆盖少量反例。**\n",
    "\n",
    "然而，由于搜索空间呈指数大小，要找到一个最佳的规则的计算开销很大。\n",
    "\n",
    "Learn-One-Rule函数通过以一种贪心的方式的增长规则来解决指数搜索问题。\n",
    "\n",
    "它产生一个初始规则$r$，并不断对该规则求精，直到满足某种终止条件为止。然后修剪该规则，以改进它的泛化误差。"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "**规则增长策略**：\n",
    "\n",
    "常见的分类规则增长策略有两种：\n",
    "\n",
    "从**一般到特殊**和**从特殊到一般。**\n",
    "\n",
    ">在从一般到特殊的策略中，先建立一个初始规则$r:\\{\\}→y$，其中左边是一个空集，右边包含目标类。\n",
    "\n",
    "该规则的质量很差，因为它覆盖训练集中的所有样例。接着加入新的合取项来提高规则的质量，直到满足终止条件为止（例如，加入的合取项已不能提高规则的质量）。"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    ">对于从特殊到一般的策略，可以随机地选择一个正例作为规则增长的初始种子。\n",
    "\n",
    "再求精步，通过删除规则的一个合取项，使其覆盖更多的正例来范化规则。重复求精步，直到满足终止条件为止（例如，当规则开始覆盖反例时为止）。"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "由于规则的贪心的方式增长，以上方法可能会产生次优规则。\n",
    "\n",
    "为了避免这种问题，可以采用束状搜索（beam search）。\n",
    "\n",
    "算法维护$k$个最佳候选规则，各候选规则各自在其前件中添加或删除合取项而独立地增长。\n",
    "\n",
    "评估候选规则的质量，选出$k$个最佳候选进入下一轮迭代。"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "**规则评估：**\n",
    "\n",
    "在规则的增长过程中，**需要一种评估度量来确定应该添加（或删除）哪个合取项**。\n",
    "\n",
    "准确率就是一个很明显的选择，因为它明确地给出了被规则正确分类的训练样例的比例。**然而把准确率作为标准的一个潜在的局限性是它没有考虑规则的覆盖率。**"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "下面的方法可以用来处理该问题："
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "（1）**可以使用统计检验剪除覆盖率较低的规则。**\n",
    "\n",
    "例如，我们可以计算下面的似然比（likelihood ratio）统计量：\n",
    "\n",
    "$$R=2\\sum_{i=1}^mf_ilog(\\frac{f_i}{e_i})$$\n",
    "\n",
    "其中，$m$是类的个数，$f_i$ 是被规则覆盖的类$i$的样本的观测频率，$e_i$ 是规则作随机猜想的期望频率。注意R是满足自由度为$m-1$的$χ^2$分布。较大的R值说明该规则做出的正确预测数显著地大于随机猜测的结果。\n",
    "\n",
    "（2）**可以使用一种考虑规则覆盖率的评估度量。**\n",
    "\n",
    "考虑如下评估度量：\n",
    "$$Laplace = \\frac{f_++1}{n+k}$$\n",
    "\n",
    "$$m估计=\\frac{f_++kp_+}{n+k}$$\n",
    "\n",
    "其中n是规则覆盖的样例数，f+是规则覆盖的正例数，k是类的总数，p+是正类的先验概率。注意当p+=1/k时，m估计等价于Laplace度量。\n",
    "\n",
    "（3）**另一种可以使用的评估度量是考虑规则的支持度计数的评估度量。**\n",
    "\n",
    "FOIL信息增益就是一种这样的度量。规则的支持度计数对应于它所覆盖的正例数。\n",
    "\n",
    "假设规则$r : A→+$覆盖$p_0$个正例和$n_0$个反例。增加新的合取项B，扩展后的规则$r' : A∧B→+$覆盖$p_1$个正例和$n_1$个反例。\n",
    "\n",
    "根据以上信息，扩展后规则的FOIL信息增益定义为：\n",
    "\n",
    "$$FOIL信息增益=p_1*(log_2\\frac{p_1}{p_1+n_1}-log_2\\frac{p_0}{p_0+n_0})$$\n",
    "\n",
    "由于该度量与$p1$和$p1/p1+n1$成正比，因此它更倾向于选择那些高支持度计数和高准确率的规则。\n",
    "\n",
    "规则减枝 \n",
    "\n",
    "可以对Learn-One-Rule函数产生的规则进行减枝，以改善它们的泛化误差。"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "# 2. 顺序覆盖基本原理\n",
    "\n",
    "\n",
    "规则提取出来后，顺序覆盖算法必须删除该规则所覆盖的所有正例和反例。\n"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "# 3. RIPPER算法"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "为了阐明规则提取的直接方法，考虑一种广泛使用的规则归纳算法，叫作RIPPER算法。\n",
    "\n",
    "该算法的复杂度几乎线性地随训练样例的数目增长，并且特别适合为类分布不平衡的数据集建立模型。\n",
    "\n",
    "RIPPER也能很好地处理噪声数据集，因为它使用一个确认数据集来防止模型过分拟合。"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "对两类问题，RIPPER算法选择以多数类作为默认类，并为预测少数类学习规则。\n",
    "\n",
    "对于多类问题，先按类的频率对类进行排序，设$（y1,y2,…,yc）$是排序后的类，其中$y_1$是最不频繁的类，$y_c$是最频繁的类。\n",
    "\n",
    "第一次迭代中，把属于$y_1$的样例标记为正例，而把其他类的样例标记为反例，使用顺序覆盖算法产生区分正例和反例的规则。\n",
    "\n",
    "接下来，RIPPER提取区分$y_2$和其他类的规则。重复该过程，直到剩下类$y_c$，此时$y_c$作为默认类。"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "**规则增长**\n",
    "\n",
    "RIPPER算法使用从一般到特殊的策略进行规则增长，使用FOIL信息增益来选择最佳合取项添加到规则前件中。\n",
    "\n",
    "当规则开始覆盖反例时，停止添加合取项。\n",
    "\n",
    "新规则根据其在确认集上的性能进行减枝。\n",
    "\n",
    "计算下面的度量来确定规则是否需要减枝：$(p-n)/(p+n)$，其中p和n分别是被规则覆盖的确认集中的正例和反例数目，关于规则在确认集上的准确率，该度量是单调的。\n",
    "\n",
    "如果减枝后该度量增加，那么就去掉该合取项。减枝是从最后添加的合取项开始的。\n",
    "\n",
    "例如给定规则$ABCD→y$，RIPPER算法先检查D是否应该减枝，然后是CD、BCD等。\n",
    "\n",
    "尽管原来的规则仅覆盖正例，但是减枝后的规则可能会覆盖训练集中的一些反例。"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "**建立规则集** \n",
    "\n",
    "规则生成后，它所覆盖的所有正例和反例都要被删除。只要该规则不违反基于最小描述长度的终止条件，就把它添加到规则集中。\n",
    "\n",
    "如果新规则把规则集的总描述长度增加了至少d个比特位，那么RIPPER就停止把该规则加入到规则集（默认的d是64位）。\n",
    "\n",
    "RIPPER使用的另一个终止条件是规则在确认集上的错误率不超过50%。"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "RIPPER算法也采用其他的优化步骤来决定规则集中现存的某些规则能否被更好的规则替代。"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "# 规则提取的间接方法"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "原则上，决策树从根节点到叶节点的每一条路径都可以表示为一个分类规则。\n",
    "\n",
    "路径中的测试条件构成规则前件的合取项，叶节点的类标号赋给规则后件。\n",
    "\n",
    "注意，规则集是完全的，包含的规则是互斥的。"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "下面，介绍C4.5规则算法所采用的从决策树生成规则集的方法。\n",
    "\n",
    "**规则产生**\n",
    "\n",
    "决策树中从根节点到叶节点的每一条路径都产生一条分类规则。\n",
    "\n",
    "给定一个分类规则$r : A→y$，考虑简化后的规则 $r': A' →y$，其中A'是从A去掉一个合取项后得到的。\n",
    "\n",
    "只要简化后的规则的误差率低于原规则的误差率，就保留其中悲观误差率最低的规则。\n",
    "\n",
    "重复规则减枝步骤，直到规则的悲观误差不能再改进为止。由于某些规则在减枝后会变得相同，因此必须丢弃重复规则。"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "**规则排序** \n",
    "\n",
    "产生规则集后，C4.5规则算法使用基于类的排序方案对提取的规则定序。\n",
    "\n",
    "预测同一个类的规则分到同一个子集中。计算每个子集的总描述长度，然后各类按照总描述长度由小到大排序。\n",
    "\n",
    "具有最小描述长度的类优先级最高，因为期望它包含最好的规则集。类的总描述长度等于Lexception+g×Lmodel，其中Lexception是对误分类样例编码所需的比特位数，Lmodel是对模型编码所需要的比特位数，而g是调节参数，默认值为0.5,。\n",
    "\n",
    "调节参数的值取决于模型中冗余属性的数量，如果模型含有很多冗余属性，那么调节参数的值会很小。"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "# 基于规则的分类器的特征"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "基于规则的分类器有如下特点：\n",
    "\n",
    ">规则集的表达能力几乎等价于决策树，因为决策树可以用互斥和穷举的规则集表示。\n",
    "\n",
    ">基于规则分类器和决策树分类器都对属性空间进行直线划分，并将类指派到每个划分。\n",
    "\n",
    ">然而，如果基于规则的分类器允许一条记录触发多条规则的话，就可以构造一个更加复杂的决策边界。\n",
    "\n",
    ">基于规则的分类器通常被用来产生更易于解释的描述性模型，而模型的性能却可与决策树分类器相媲美。\n",
    "\n",
    ">被很多基于规则的分类器（如RIPPER）所采用的基于类的规则定序方法非常适于处理类分布不平衡的数据集。"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "outputs": [],
   "source": [
    "\n",
    "\n",
    "\n"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.9"
  },
  "pycharm": {
   "stem_cell": {
    "cell_type": "raw",
    "source": [],
    "metadata": {
     "collapsed": false
    }
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}